package cn.huiyunche.base.service.interfaces.impl;

import cn.huiyunche.base.service.constant.YeePayConstant;
import cn.huiyunche.base.service.enums.YeepayCmdEnum;
import cn.huiyunche.base.service.enums.YeepayNotifyStatusEnum;
import cn.huiyunche.base.service.framework.utils.HYCUtils;
import cn.huiyunche.base.service.interfaces.SendMessageService;
import cn.huiyunche.base.service.interfaces.YeepayService;
import cn.huiyunche.base.service.mappers.CWithdrawLogMapper;
import cn.huiyunche.base.service.mappers.CYeepaynotifyMapper;
import cn.huiyunche.base.service.model.CWithdrawLog;
import cn.huiyunche.base.service.model.CWithdrawLogExample;
import cn.huiyunche.base.service.model.CYeepaynotify;
import cn.huiyunche.base.service.vo.Result;
import cn.huiyunche.base.service.vo.YeepayNotifyXmlRequestVo;
import cn.huiyunche.base.service.yeepay.utils.Digest;
import com.cfca.util.pki.PKIException;
import com.cfca.util.pki.api.CertUtil;
import com.cfca.util.pki.api.KeyUtil;
import com.cfca.util.pki.api.SignatureUtil;
import com.cfca.util.pki.cert.X509Cert;
import com.cfca.util.pki.cipher.JCrypto;
import com.cfca.util.pki.cipher.JKey;
import com.cfca.util.pki.cipher.Session;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.jdom.Element;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.List;

/**
 * 易宝回调接口
 *
 * @author hdy [Tuffy]
 */
@Service
public class YeepayServiceImpl implements YeepayService {

    private SignatureUtil signUtil = null;
    private Session tempsession = null;
    private final String yeepaySuccess = "S";

    @Autowired
    private CWithdrawLogMapper cWithdrawLogMapper = null;

    @Autowired
    private CYeepaynotifyMapper cYeepaynotifyMapper = null;

    @Autowired
    private SendMessageService sendMessageService = null;

    @Override
    public void yeepNotify(HttpServletRequest request, HttpServletResponse response) throws IOException, PKIException, DocumentException {
        String charEncoding = request.getCharacterEncoding();
        String xml = IOUtils.toString(request.getInputStream(), charEncoding);
        Result<YeepayNotifyXmlRequestVo> r = parseYeepayRequestXml(xml);
        String hmacSre = this.setNotifyHmac(r.getData(), r.isSuccess());
        response.getOutputStream().write(hmacSre.toString().getBytes());
    }

    @Override
    public Result<YeepayNotifyXmlRequestVo> parseYeepayRequestXml(String xmlStr) throws IOException, PKIException, DocumentException {
        Result<YeepayNotifyXmlRequestVo> r = new Result<>(false);
        Document xml = DocumentHelper.parseText(xmlStr);
        YeepayNotifyXmlRequestVo ynxrv = new YeepayNotifyXmlRequestVo(xml);
        r.setData(ynxrv);

        // 保存回写日志
        CYeepaynotify cy = new CYeepaynotify();
        BeanCopier beanCopierT = BeanCopier.create(YeepayNotifyXmlRequestVo.class, CYeepaynotify.class, false);
        beanCopierT.copy(ynxrv, cy, null);
        this.getCYeepaynotifyMapper().insert(cy);

        //第五步:对服务器响应报文进行验证签名
        boolean sigerCertFlag = false;
        signUtil = new SignatureUtil();
        JCrypto jcrypto = null;
        String hmacStr = ynxrv.getHmac();
        if (StringUtils.isNotBlank(hmacStr)) {
            jcrypto = JCrypto.getInstance();
            jcrypto.initialize(JCrypto.JSOFT_LIB, null);
            tempsession = jcrypto.openSession(JCrypto.JSOFT_LIB);
            sigerCertFlag = signUtil.p7VerifySignMessage(hmacStr.getBytes(), tempsession);
            String backmd5hmac = "";
            if (sigerCertFlag) {
                System.out.println("证书验签成功");
                backmd5hmac = new String(signUtil.getSignedContent());
                System.out.println("证书验签获得的MD5签名数据为----" + backmd5hmac);
                System.out.println("证书验签获得的证书dn为----" + new String(signUtil.getSigerCert()[0].getSubject()));
                if (this.signNotifyHmac(ynxrv, backmd5hmac)) {
                    // 第八步:.......加上业务逻辑
                    this.updateCWithdrawLog(ynxrv);
                    r.setSuccess(true);
                    r.setData(ynxrv);
                    return r;
                }
            }
        }
        return r;
    }

    @Override
    public String signHmac(String hmacStr) throws PKIException, UnsupportedEncodingException {
        String ALGORITHM = SignatureUtil.SHA1_RSA;
        JCrypto jcrypto = null;
        if (tempsession == null) {
            // 初始化加密库，获得会话session
            // 多线程的应用可以共享一个session,不需要重复,只需初始化一次
            // 初始化加密库并获得session。
            // 系统退出后要jcrypto.finalize()，释放加密库
            jcrypto = JCrypto.getInstance();
            jcrypto.initialize(JCrypto.JSOFT_LIB, null);
            tempsession = jcrypto.openSession(JCrypto.JSOFT_LIB);
            String classPath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
            String keyString = classPath + File.separator + HYCUtils.getConstantPropertiesLoader().getProperty(YeePayConstant.YEEPAY_KEY_PFX_NAME);
            String certString = classPath + File.separator + HYCUtils.getConstantPropertiesLoader().getProperty(YeePayConstant.YEEPAY_KEY_PFX_NAME);
            // 证书添加
            JKey jkey = KeyUtil.getPriKey(keyString, HYCUtils.getConstantPropertiesLoader().getProperty(YeePayConstant.YEEPAY_KEY_PASSWORD));
            X509Cert cert = CertUtil.getCert(certString, HYCUtils.getConstantPropertiesLoader().getProperty(YeePayConstant.YEEPAY_CERTP_PASSWORD));
            System.out.println(cert.getSubject());
            X509Cert[] cs = new X509Cert[1];
            cs[0] = cert;
            String signMessage = "";
            // 第二步:对请求的串进行MD5对数据进行签名
            String yphs = Digest.hmacSign(hmacStr);
            signUtil = new SignatureUtil();
            byte[] b64SignData;
            // 第三步:对MD5签名之后数据调用CFCA提供的api方法用商户自己的数字证书进行签名
            b64SignData = signUtil.p7SignMessage(true, yphs.getBytes(), ALGORITHM, jkey, cs, tempsession);
            if (jcrypto != null) {
                jcrypto.finalize(com.cfca.util.pki.cipher.JCrypto.JSOFT_LIB, null);
            }
            signMessage = new String(b64SignData, "UTF-8");
            // System.out.println("经过md5和数字证书签名之后的数据为---||" + signMessage + "||");
            return signMessage;
        }
        return null;
    }

    /**
     * 验证签名
     *
     * @param ynxrv
     * @param backmd5hmac
     * @return
     * @throws PKIException
     */
    private boolean signNotifyHmac(YeepayNotifyXmlRequestVo ynxrv, String backmd5hmac) throws PKIException {
        // 按顺序将 cmd、mer_Id、batch_No、 order_Id、status、message 的参数值、商户密钥组成字符串
        StringBuffer sb = new StringBuffer("");
        sb.append(ynxrv.getCmd()).append(ynxrv.getMetid()).append(ynxrv.getBatchNo()).append(ynxrv.getOrderId())
                .append(ynxrv.getStatus()).append(ynxrv.getMessage())
                .append(HYCUtils.getConstantPropertiesLoader().getProperty(YeePayConstant.YEEPAY_GROUP_CA));
        String newmd5hmac = Digest.hmacSign(sb.toString());
        System.out.println("提交返回源数据为---||" + backmd5hmac + "||");
        System.out.println("经过md5签名后的验证返回hmac为---||" + newmd5hmac + "||");
        System.out.println("提交返回的hmac为---||" + backmd5hmac + "||");
        if (newmd5hmac.equals(backmd5hmac)) {
            System.out.println("md5验签成功");
            // 第七步:判断该证书DN是否为易宝
            if (signUtil.getSigerCert()[0].getSubject().toUpperCase().indexOf("OU=YEEPAY,") > 0) {
                System.out.println("证书DN是易宝的");
                return true;
            }
            System.out.println("证书DN不是易宝的");
        }
        return false;
    }

    /**
     * 返回签名
     *
     * @param batchNo
     * @param orderId
     * @param isOk
     * @return
     * @throws PKIException
     * @throws UnsupportedEncodingException
     */
    private String setNotifyHmac(YeepayNotifyXmlRequestVo ynxrv, boolean isOk) throws UnsupportedEncodingException, PKIException {

        String ca = HYCUtils.getConstantPropertiesLoader().getProperty(YeePayConstant.YEEPAY_GROUP_CA);
        org.jdom.Document xml = new org.jdom.Document();
        Element root = new Element("data");

        Element cmd = new Element("cmd");
        cmd.setText(YeepayCmdEnum.NOTIFY.getValue());
        root.addContent(cmd);

        Element merId = new Element("mer_Id");
        merId.setText(ynxrv.getMetid());
        root.addContent(merId);

        Element batch_No = new Element("batch_No");
        batch_No.setText(ynxrv.getBatchNo());
        root.addContent(batch_No);

        Element order_Id = new Element("order_Id");
        order_Id.setText(ynxrv.getOrderId());
        root.addContent(order_Id);

        Element retCode = new Element("ret_Code");
        if (isOk) {
            retCode.setText(YeepayNotifyStatusEnum.S.getValue());
        } else {
            retCode.setText(YeepayNotifyStatusEnum.F.getValue());
        }
        root.addContent(retCode);

        //cmd、mer_Id、batch_No、 order_Id 、ret_Code +商户密钥组
        StringBuffer sb = new StringBuffer();
        sb.append(cmd.getText()).append(merId.getText()).append(ynxrv.getBatchNo()).append(ynxrv.getOrderId()).append(retCode.getText()).append(ca);
        Element hmac = new Element("hmac");
        hmac.setText(this.signHmac(sb.toString()));
        root.addContent(hmac);

        xml.addContent(root);
        XMLOutputter XMLOut = new XMLOutputter();
        Format f = Format.getPrettyFormat();
        f.setEncoding("GBK");
        XMLOut.setFormat(f);
        return XMLOut.outputString(xml);
    }

    /**
     * 修改回调记录
     *
     * @param orderId 订单id
     * @param status  状态
     */
    private void updateCWithdrawLog(YeepayNotifyXmlRequestVo ynxrv) {
        CWithdrawLogExample cwle = new CWithdrawLogExample();
        cwle.createCriteria().andOrderIdEqualTo(ynxrv.getOrderId());
        List<CWithdrawLog> list = this.getCWithdrawLogMapper().selectByExample(cwle);
        CWithdrawLog cwl = list.size() > 0 ? list.get(0) : null;
        if (null != cwl) {
            String status = ynxrv.getStatus();
            // 已经回调
            if (null != cwl.getCallbackRetTime()) {
                return;
            }
            // 更新日志
            cwl.setCallbackRetCode(status);
            cwl.setCallbackRetTime(new Date());
            this.getCWithdrawLogMapper().updateByPrimaryKeySelective(cwl);
            // 成功修改业务
            if (status.equals(yeepaySuccess)) {
                String cardNo = cwl.getCardno();
                this.sendMessageService.sendYeepayNotifySuccessMessage(cardNo.substring(cardNo.length() - 4, cardNo.length()), cwl.getPhone());
                return;
            }
            // 回调失败
            String cardNo = cwl.getCardno();
            this.sendMessageService.sendYeepayNotifyErrorMessage(cardNo.substring(cardNo.length() - 4, cardNo.length()), cwl.getPhone(), ynxrv.getMessage());
        }

    }

    private CWithdrawLogMapper getCWithdrawLogMapper() {
        return this.cWithdrawLogMapper;
    }

    private CYeepaynotifyMapper getCYeepaynotifyMapper() {
        return this.cYeepaynotifyMapper;
    }
}
